/**
 *
 * @param {Function} reducer (state, action) => newState
 * @param {object} preloadedState 初始状态
 * @param {Function} enhancer 中间件
 * @returns
 */
export function createStore(reducer, preloadedState, enhancer) {
  // 增强dispatch函数 start
  if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
    enhancer = preloadedState;
    preloadedState = undefined;
  }
  if (typeof enhancer !== "undefined") {
    // 判断 enhancer 不是一个函数
    if (typeof enhancer !== "function") {
      // 抛出一个异常 (enhancer 必须是一个函数)
      throw new Error("Expected the enhancer to be a function.");
    }
    // 调用 enhancer ,返回一个增强版的 store creator
    return enhancer(createStore)(reducer, preloadedState);
    // 函数柯里化
    // function enhancer(createStore) {
    //     return (reducer, preloadedState) => {
    //         return createStore(reducer, preloadedState)
    //     }
    // }
  }
  let subscribe_fns = new Set();
  let state = preloadedState;
  function getState() {
    return state;
  }
  function subscribe(listener_fn) {
    subscribe_fns.add(listener_fn);
    //  返回一个取消订阅的方法
    return function unSubscribe() {
      const index = subscribe_fns.indexOf(listener_fn);
      subscribe_fns.splice(index, 1);
    };
  }
  function dispatch(action) {
    state = reducer(state, action);
    for (let fn of subscribe_fns) {
      fn();
    }
    // return action给中间件使用
    return action;
  }

  // 初始化state
  dispatch({});

  return {
    getState,
    subscribe,
    dispatch,
  };
}

export function combineReducers(reducers) {
  return function reducer(state = {}, action) {
    let newState = {};
    let hasChanged = false;
    for (let [key, rdc] of Object.entries(reducers)) {
      const catchState = state[key];
      const newStateForKey = rdc(catchState, action);
      newState[key] = newStateForKey;
      hasChanged = hasChanged || newStateForKey !== catchState;
    }
    return hasChanged ? newState : state;
  };
}
// 洋葱模型，前一个函数的入参是后一个函数的出参f1(f2(f3(dispatch)))
function compose(...chains) {
  // initialValue 可选 （表示初始值，作为第一次调用 callback 的第一个参数。）
  // 提供初始值，cur 从数组第一项开始，若不提供初始值，则 cur 从第二项开始执行，对应的第一次 prev 是数组第一项的值
  const arr = [(next) => (action) => next(action)];
  return chains.reduce((prev, cur) => {
    return (...args) => prev(cur.apply(undefined, ...args));
  });
}

// 中间件函数
// applyMiddleware就是实现enhancer(createStore)(reducer, preloadedState) = applyMiddleware(...middlewares)
export function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState);
    let newDispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      );
    };

    const chain = middlewares.map((middleware) =>
      middleware({
        getState: store.getState,
        dispatch: (...args) => newDispatch(...args),
      })
    );
    // 转换为
    // newDispatch = middleware1(middleware2(middleware3.apply(undefined, store.dispatch)))
    newDispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispatch: newDispatch,
    };
  };
}

// 中间件的使用方式
// 其实applyMiddleware就是Redux的一个原生方法，将所有中间件组成一个数组，依次执行。
// next就是dispatch
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) =>
    (next) => 
    (action) => {
      if (typeof action === "function") {
        return action(dispatch, getState, extraArgument);
      }
      return next(action);
    };
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

const reducer = combineReducers(reducers);
// 中间件多了可以当做参数依次传进去
const store = createStore(reducer, applyMiddleware(thunk, logger));

const fetchApi = (...args) => (dispatch, getState) => {
  setTimeout(() => {
    dispatch({ type: "xx", payload: "good" });
  }, 7000);
};

dispatch(fetchApi("xxx"));

